| Soares Chen | 6d8970d | 2017-06-12 16:02:23 | [diff] [blame] | 1 | <!doctype html> | 
|  | 2 | <meta charset=utf-8> | 
|  | 3 | <title>RTCPeerConnection.prototype.ondatachannel</title> | 
|  | 4 | <script src="/resources/testharness.js"></script> | 
|  | 5 | <script src="/resources/testharnessreport.js"></script> | 
|  | 6 | <script src="RTCPeerConnection-helper.js"></script> | 
|  | 7 | <script> | 
|  | 8 | 'use strict'; | 
|  | 9 |  | 
|  | 10 | // Test is based on the following editor draft: | 
|  | 11 | // https://w3c.github.io/webrtc-pc/archives/20170605/webrtc.html | 
|  | 12 |  | 
|  | 13 | // The following helper functions are called from RTCPeerConnection-helper.js: | 
|  | 14 | // exchangeIceCandidates | 
|  | 15 | // doSignalingHandshake | 
|  | 16 |  | 
|  | 17 | /* | 
|  | 18 | 6.2. RTCDataChannel | 
|  | 19 | When an underlying data transport is to be announced | 
|  | 20 | 2. Let channel be a newly created RTCDataChannel object. | 
|  | 21 | 5. Set channel's readyState attribute to connecting. | 
|  | 22 | 6. Fire a datachannel event named datachannel with channel at the | 
|  | 23 | RTCPeerConnection object. | 
|  | 24 |  | 
|  | 25 | 6.3. RTCDataChannelEvent | 
|  | 26 | Firing a datachannel event named e with a RTCDataChannel channel means | 
|  | 27 | that an event with the name e, which does not bubble (except where | 
|  | 28 | otherwise stated) and is not cancelable (except where otherwise stated), | 
|  | 29 | and which uses the RTCDataChannelEvent interface with the channel | 
|  | 30 | attribute set to channel, must be created and dispatched at the given | 
|  | 31 | target. | 
|  | 32 |  | 
|  | 33 | interface RTCDataChannelEvent : Event { | 
|  | 34 | readonly attribute RTCDataChannel channel; | 
|  | 35 | }; | 
|  | 36 | */ | 
|  | 37 | async_test(t => { | 
|  | 38 | const localPc = new RTCPeerConnection(); | 
|  | 39 | const remotePc = new RTCPeerConnection(); | 
|  | 40 |  | 
|  | 41 | let eventCount = 0; | 
|  | 42 |  | 
|  | 43 | const onDataChannel = t.step_func_done(event => { | 
|  | 44 | eventCount++; | 
|  | 45 | assert_equals(eventCount, 1, | 
|  | 46 | 'Expect data channel event to fire exactly once'); | 
|  | 47 |  | 
|  | 48 | assert_true(event instanceof RTCDataChannelEvent, | 
|  | 49 | 'Expect event to be instance of RTCDataChannelEvent'); | 
|  | 50 |  | 
|  | 51 | assert_equals(event.bubbles, false); | 
|  | 52 | assert_equals(event.cancelable, false); | 
|  | 53 |  | 
|  | 54 | const { channel } = event; | 
|  | 55 | assert_true(channel instanceof RTCDataChannel, | 
|  | 56 | 'Expect channel to be instance of RTCDataChannel'); | 
|  | 57 |  | 
|  | 58 | const { readyState } = channel; | 
|  | 59 |  | 
|  | 60 | // The spec requires readyState to be connecting at first, | 
|  | 61 | // but it may quickly change to open before the callback | 
|  | 62 | // is invoked, especially with local connections. | 
|  | 63 | assert_true(readyState === 'connecting' || readyState === 'open', | 
|  | 64 | 'Expect channel ready state to be either connecting or open'); | 
|  | 65 | }); | 
|  | 66 |  | 
|  | 67 | localPc.createDataChannel('test'); | 
|  | 68 |  | 
|  | 69 | remotePc.addEventListener('datachannel', onDataChannel); | 
|  | 70 | exchangeIceCandidates(localPc, remotePc); | 
|  | 71 | doSignalingHandshake(localPc, remotePc); | 
|  | 72 |  | 
|  | 73 | }, 'datachannel event should fire when new data channel is announced to the remote peer'); | 
|  | 74 |  | 
|  | 75 | /* | 
|  | 76 | 6.2. RTCDataChannel | 
|  | 77 | interface RTCDataChannel : EventTarget { | 
|  | 78 | readonly attribute USVString label; | 
|  | 79 | readonly attribute boolean ordered; | 
|  | 80 | readonly attribute unsigned short? maxPacketLifeTime; | 
|  | 81 | readonly attribute unsigned short? maxRetransmits; | 
|  | 82 | readonly attribute USVString protocol; | 
|  | 83 | readonly attribute boolean negotiated; | 
|  | 84 | readonly attribute unsigned short? id; | 
|  | 85 | readonly attribute RTCPriorityType priority; | 
|  | 86 | readonly attribute RTCDataChannelState readyState; | 
|  | 87 | ... | 
|  | 88 | }; | 
|  | 89 |  | 
|  | 90 | When an underlying data transport is to be announced | 
|  | 91 | 3. Let configuration be an information bundle received from the | 
|  | 92 | other peer as a part of the process to establish the underlying | 
|  | 93 | data transport described by the WebRTC DataChannel Protocol | 
|  | 94 | specification [RTCWEB-DATA-PROTOCOL]. | 
|  | 95 | 4. Initialize channel's label, ordered, maxPacketLifeTime, | 
|  | 96 | maxRetransmits, protocol, negotiated and id attributes to their | 
|  | 97 | corresponding values in configuration. | 
|  | 98 | */ | 
|  | 99 | async_test(t => { | 
|  | 100 | const localPc = new RTCPeerConnection(); | 
|  | 101 | const remotePc = new RTCPeerConnection(); | 
|  | 102 |  | 
|  | 103 | const onDataChannel = t.step_func_done(event => { | 
|  | 104 | const remoteChannel = event.channel; | 
|  | 105 | assert_true(remoteChannel instanceof RTCDataChannel, | 
|  | 106 | 'Expect channel to be instance of RTCDataChannel'); | 
|  | 107 |  | 
|  | 108 | assert_equals(remoteChannel.label, 'test'); | 
|  | 109 | assert_equals(remoteChannel.id, 8); | 
|  | 110 | assert_equals(remoteChannel.ordered, false); | 
|  | 111 | assert_equals(remoteChannel.maxRetransmits, 1); | 
|  | 112 | assert_equals(remoteChannel.protocol, 'custom'); | 
|  | 113 | assert_equals(remoteChannel.priority, 'high'); | 
|  | 114 | }); | 
|  | 115 |  | 
|  | 116 | const localChannel = localPc.createDataChannel('test', { | 
|  | 117 | id: 8, | 
|  | 118 | ordered: false, | 
|  | 119 | maxRetransmits: 1, | 
|  | 120 | protocol: 'custom', | 
|  | 121 | priority: 'high' | 
|  | 122 | }); | 
|  | 123 |  | 
|  | 124 | assert_equals(localChannel.label, 'test'); | 
|  | 125 | assert_equals(localChannel.id, 8); | 
|  | 126 | assert_equals(localChannel.ordered, false); | 
|  | 127 | assert_equals(localChannel.maxRetransmits, 1); | 
|  | 128 | assert_equals(localChannel.protocol, 'custom'); | 
|  | 129 | assert_equals(localChannel.priority, 'high'); | 
|  | 130 |  | 
|  | 131 | remotePc.addEventListener('datachannel', onDataChannel); | 
|  | 132 | exchangeIceCandidates(localPc, remotePc); | 
|  | 133 | doSignalingHandshake(localPc, remotePc); | 
|  | 134 | }, 'Data channel created on remote peer should match the same configuration as local peer'); | 
|  | 135 |  | 
|  | 136 | /* | 
|  | 137 | 6.2. RTCDataChannel | 
|  | 138 | Dictionary RTCDataChannelInit Members | 
|  | 139 | negotiated | 
|  | 140 | The default value of false tells the user agent to announce the | 
|  | 141 | channel in-band and instruct the other peer to dispatch a corresponding | 
|  | 142 | RTCDataChannel object. If set to true, it is up to the application | 
|  | 143 | to negotiate the channel and create a RTCDataChannel object with the | 
|  | 144 | same id at the other peer. | 
|  | 145 | */ | 
|  | 146 | async_test(t => { | 
|  | 147 | const localPc = new RTCPeerConnection(); | 
|  | 148 | const remotePc = new RTCPeerConnection(); | 
|  | 149 |  | 
|  | 150 | const onDataChannel = t.unreached_func('datachannel event should not be fired'); | 
|  | 151 |  | 
|  | 152 | localPc.createDataChannel('test', { | 
|  | 153 | negotiated: true | 
|  | 154 | }); | 
|  | 155 |  | 
|  | 156 | remotePc.addEventListener('datachannel', onDataChannel); | 
|  | 157 | exchangeIceCandidates(localPc, remotePc); | 
|  | 158 | doSignalingHandshake(localPc, remotePc); | 
|  | 159 |  | 
|  | 160 | t.step_timeout(t.step_func_done(), 200); | 
|  | 161 |  | 
|  | 162 | }, 'Data channel created with negotiated set to true should not fire datachannel event on remote peer'); | 
|  | 163 |  | 
|  | 164 | /* | 
|  | 165 | Non-testable | 
|  | 166 | 6.2. RTCDataChannel | 
|  | 167 | When an underlying data transport is to be announced | 
|  | 168 | 1. If the associated RTCPeerConnection object's [[isClosed]] slot | 
|  | 169 | is true, abort these steps. | 
|  | 170 |  | 
|  | 171 | The above step is not testable because to reach it we would have to | 
|  | 172 | close the peer connection after the remote peer call | 
|  | 173 | setLocalDescription(answer) but before the underlying data transport | 
|  | 174 | is connected. This require the promise callback for setLocalDescription() | 
|  | 175 | to be called at the right moment, which is not always possible. | 
|  | 176 | */ | 
|  | 177 | </script> |